package com.smart.community.commons.websocket.monitor;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * WebSocket监控和统计
 * 
 * @author Wu.Liang
 * @since 2025-01-30
 */
@Component
@Slf4j
public class WebSocketMonitor {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 监控指标
    private final AtomicLong totalConnections = new AtomicLong(0);
    private final AtomicLong activeConnections = new AtomicLong(0);
    private final AtomicLong totalMessages = new AtomicLong(0);
    private final AtomicLong successMessages = new AtomicLong(0);
    private final AtomicLong failedMessages = new AtomicLong(0);
    private final AtomicLong totalRooms = new AtomicLong(0);
    private final AtomicLong totalUsers = new AtomicLong(0);

    // 性能指标
    private final AtomicLong avgResponseTime = new AtomicLong(0);
    private final AtomicLong maxResponseTime = new AtomicLong(0);
    private final AtomicLong minResponseTime = new AtomicLong(Long.MAX_VALUE);

    // 错误统计
    private final Map<String, AtomicLong> errorCounts = new ConcurrentHashMap<>();
    private final Map<String, AtomicLong> messageTypeCounts = new ConcurrentHashMap<>();

    // 时间窗口统计
    private final Map<String, TimeWindowStats> timeWindowStats = new ConcurrentHashMap<>();

    // Redis键前缀
    private static final String MONITOR_PREFIX = "websocket:monitor:";
    private static final String STATS_PREFIX = "websocket:stats:";

    /**
     * 记录连接建立
     */
    public void recordConnection() {
        totalConnections.incrementAndGet();
        activeConnections.incrementAndGet();
        updateStats("connections", 1);
    }

    /**
     * 记录连接断开
     */
    public void recordDisconnection() {
        activeConnections.decrementAndGet();
        updateStats("disconnections", 1);
    }

    /**
     * 记录消息发送
     */
    public void recordMessage(String messageType, boolean success, long responseTime) {
        totalMessages.incrementAndGet();
        
        if (success) {
            successMessages.incrementAndGet();
        } else {
            failedMessages.incrementAndGet();
        }

        // 更新响应时间统计
        updateResponseTimeStats(responseTime);

        // 更新消息类型统计
        messageTypeCounts.computeIfAbsent(messageType, k -> new AtomicLong(0)).incrementAndGet();

        // 更新时间窗口统计
        updateTimeWindowStats(messageType, success, responseTime);

        updateStats("messages", 1);
    }

    /**
     * 记录错误
     */
    public void recordError(String errorType) {
        errorCounts.computeIfAbsent(errorType, k -> new AtomicLong(0)).incrementAndGet();
        updateStats("errors", 1);
    }

    /**
     * 记录房间创建
     */
    public void recordRoomCreated() {
        totalRooms.incrementAndGet();
        updateStats("rooms", 1);
    }

    /**
     * 记录房间删除
     */
    public void recordRoomDeleted() {
        totalRooms.decrementAndGet();
        updateStats("rooms_deleted", 1);
    }

    /**
     * 记录用户上线
     */
    public void recordUserOnline() {
        totalUsers.incrementAndGet();
        updateStats("users_online", 1);
    }

    /**
     * 记录用户下线
     */
    public void recordUserOffline() {
        totalUsers.decrementAndGet();
        updateStats("users_offline", 1);
    }

    /**
     * 获取监控指标
     */
    public MonitorMetrics getMetrics() {
        MonitorMetrics metrics = new MonitorMetrics();
        metrics.setTotalConnections(totalConnections.get());
        metrics.setActiveConnections(activeConnections.get());
        metrics.setTotalMessages(totalMessages.get());
        metrics.setSuccessMessages(successMessages.get());
        metrics.setFailedMessages(failedMessages.get());
        metrics.setTotalRooms(totalRooms.get());
        metrics.setTotalUsers(totalUsers.get());
        metrics.setAvgResponseTime(avgResponseTime.get());
        metrics.setMaxResponseTime(maxResponseTime.get());
        metrics.setMinResponseTime(minResponseTime.get() == Long.MAX_VALUE ? 0 : minResponseTime.get());
        metrics.setSuccessRate(calculateSuccessRate());
        metrics.setTimestamp(LocalDateTime.now());

        // 添加错误统计
        Map<String, Long> errorStats = new ConcurrentHashMap<>();
        errorCounts.forEach((errorType, count) -> errorStats.put(errorType, count.get()));
        metrics.setErrorCounts(errorStats);

        // 添加消息类型统计
        Map<String, Long> messageTypeStats = new ConcurrentHashMap<>();
        messageTypeCounts.forEach((messageType, count) -> messageTypeStats.put(messageType, count.get()));
        metrics.setMessageTypeCounts(messageTypeStats);

        return metrics;
    }

    /**
     * 获取时间窗口统计
     */
    public Map<String, TimeWindowStats> getTimeWindowStats() {
        return new ConcurrentHashMap<>(timeWindowStats);
    }

    /**
     * 重置监控指标
     */
    public void resetMetrics() {
        totalConnections.set(0);
        activeConnections.set(0);
        totalMessages.set(0);
        successMessages.set(0);
        failedMessages.set(0);
        totalRooms.set(0);
        totalUsers.set(0);
        avgResponseTime.set(0);
        maxResponseTime.set(0);
        minResponseTime.set(Long.MAX_VALUE);
        errorCounts.clear();
        messageTypeCounts.clear();
        timeWindowStats.clear();

        log.info("WebSocket监控指标已重置");
    }

    /**
     * 保存监控指标到Redis
     */
    @Scheduled(fixedRate = 60000) // 每分钟保存一次
    public void saveMetricsToRedis() {
        try {
            MonitorMetrics metrics = getMetrics();
            String metricsKey = MONITOR_PREFIX + "metrics:" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
            redisTemplate.opsForValue().set(metricsKey, metrics, java.time.Duration.ofHours(24));

            // 保存当前指标
            String currentKey = MONITOR_PREFIX + "current";
            redisTemplate.opsForValue().set(currentKey, metrics, java.time.Duration.ofHours(1));

            log.debug("监控指标已保存到Redis");
        } catch (Exception e) {
            log.error("保存监控指标到Redis失败，错误: {}", e.getMessage(), e);
        }
    }

    /**
     * 清理过期监控数据
     */
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
    public void cleanupExpiredMetrics() {
        try {
            LocalDateTime expireTime = LocalDateTime.now().minusDays(7);
            String pattern = MONITOR_PREFIX + "metrics:*";
            
            // 这里需要根据具体的Redis实现来清理过期数据
            // 由于RedisTemplate的限制，这里只是示例
            log.info("清理过期监控数据，过期时间: {}", expireTime);
        } catch (Exception e) {
            log.error("清理过期监控数据失败，错误: {}", e.getMessage(), e);
        }
    }

    /**
     * 更新响应时间统计
     */
    private void updateResponseTimeStats(long responseTime) {
        long currentAvg = avgResponseTime.get();
        long currentMax = maxResponseTime.get();
        long currentMin = minResponseTime.get();

        // 更新平均响应时间（简单平均）
        long newAvg = (currentAvg + responseTime) / 2;
        avgResponseTime.set(newAvg);

        // 更新最大响应时间
        if (responseTime > currentMax) {
            maxResponseTime.set(responseTime);
        }

        // 更新最小响应时间
        if (responseTime < currentMin) {
            minResponseTime.set(responseTime);
        }
    }

    /**
     * 更新时间窗口统计
     */
    private void updateTimeWindowStats(String messageType, boolean success, long responseTime) {
        String windowKey = LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHH"));
        TimeWindowStats stats = timeWindowStats.computeIfAbsent(windowKey, k -> new TimeWindowStats());
        
        stats.setTotalMessages(stats.getTotalMessages() + 1);
        if (success) {
            stats.setSuccessMessages(stats.getSuccessMessages() + 1);
        } else {
            stats.setFailedMessages(stats.getFailedMessages() + 1);
        }
        
        stats.setTotalResponseTime(stats.getTotalResponseTime() + responseTime);
        if (responseTime > stats.getMaxResponseTime()) {
            stats.setMaxResponseTime(responseTime);
        }
        if (responseTime < stats.getMinResponseTime()) {
            stats.setMinResponseTime(responseTime);
        }
        
        stats.setAvgResponseTime(stats.getTotalResponseTime() / stats.getTotalMessages());
        stats.setTimestamp(LocalDateTime.now());
    }

    /**
     * 计算成功率
     */
    private double calculateSuccessRate() {
        long total = totalMessages.get();
        if (total == 0) {
            return 0.0;
        }
        return (double) successMessages.get() / total * 100;
    }

    /**
     * 更新统计信息
     */
    private void updateStats(String type, long count) {
        try {
            String statsKey = STATS_PREFIX + type + ":" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
            redisTemplate.opsForValue().increment(statsKey, count);
            redisTemplate.expire(statsKey, java.time.Duration.ofDays(30));
        } catch (Exception e) {
            log.warn("更新统计信息失败，类型: {}, 错误: {}", type, e.getMessage());
        }
    }

    /**
     * 监控指标数据类
     */
    @Data
    public static class MonitorMetrics {
        private long totalConnections;
        private long activeConnections;
        private long totalMessages;
        private long successMessages;
        private long failedMessages;
        private long totalRooms;
        private long totalUsers;
        private long avgResponseTime;
        private long maxResponseTime;
        private long minResponseTime;
        private double successRate;
        private LocalDateTime timestamp;
        private Map<String, Long> errorCounts;
        private Map<String, Long> messageTypeCounts;
    }

    /**
     * 时间窗口统计数据类
     */
    @Data
    public static class TimeWindowStats {
        private long totalMessages;
        private long successMessages;
        private long failedMessages;
        private long totalResponseTime;
        private long maxResponseTime;
        private long minResponseTime = Long.MAX_VALUE;
        private long avgResponseTime;
        private LocalDateTime timestamp;
    }
}
